package org.finesys.chat.core.base.document.loader;

import lombok.extern.slf4j.Slf4j;
import org.finesys.chat.core.base.document.Document;
import org.finesys.chat.core.base.document.parser.DocumentParser;
import org.finesys.chat.core.base.document.parser.TextDocumentParser;
import org.finesys.chat.core.base.document.source.FileSystemSource;
import org.finesys.common.core.util.Utils;

import java.io.IOException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

@Slf4j
public class FileSystemDocumentLoader {

    private static final DocumentParser DOCUMENT_PARSER = Utils.getOrDefault(DocumentParserLoader.loadDocumentParser(), new TextDocumentParser());

    public FileSystemDocumentLoader() {
    }

    public static Document loadDocument(Path filePath, DocumentParser documentParser) {
        if (Files.isRegularFile(filePath, new LinkOption[0])) {
            throw new IllegalArgumentException(String.format("%s is not a file", filePath));
        }
        return DocumentLoader.load(FileSystemSource.from(filePath), documentParser);
    }

    public static Document loadDocument(Path filePath) {
        return loadDocument(filePath, DOCUMENT_PARSER);
    }

    public static Document loadDocument(String filePath) {
        return loadDocument(Paths.get(filePath));
    }

    public static Document loadDocument(String filePath, DocumentParser documentParser) {
        return loadDocument(Paths.get(filePath), documentParser);
    }

    public static List<Document> loadDocuments(Path directoryPath, DocumentParser documentParser) {
        if (!Files.isDirectory(directoryPath, new LinkOption[0])) {
            throw new IllegalArgumentException(String.format("%s is not a directory", directoryPath));
        }
        Stream<Path> pathStream = null;
        try {
            pathStream = Files.list(directoryPath);
            List<Document> documents = loadDocuments(pathStream, documentParser, path -> {
                return true;
            }, directoryPath);

            return documents;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {

            if (pathStream != null) {
                pathStream.close();
            }
        }
    }

    public static List<Document> loadDocuments(Path directoryPath) {
        return loadDocuments(directoryPath, DOCUMENT_PARSER);
    }

    public static List<Document> loadDocuments(String directoryPath, DocumentParser documentParser) {
        return loadDocuments(Paths.get(directoryPath), documentParser);
    }

    public static List<Document> loadDocuments(String directoryPath) {
        return loadDocuments(Paths.get(directoryPath));
    }

    public static List<Document> loadDocuments(Stream<Path> pathStream, DocumentParser documentParser, PathMatcher pathMatcher, Path pathMatcherRoot) {
        List<Document> list = new ArrayList<>();
        Stream<Path> filterPaths = pathStream.filter(path -> {
            return Files.isRegularFile(path, new LinkOption[0]);
        });
        Objects.requireNonNull(pathMatcherRoot);
        filterPaths = filterPaths.map(pathMatcherRoot::relativize);
        Objects.requireNonNull(pathMatcher);
        filterPaths = filterPaths.filter(pathMatcher::matches);

        filterPaths.map(pathMatcherRoot::resolve).forEach(path -> {
            try {
                Document document = loadDocument(path, documentParser);
                list.add(document);
            } catch (Exception e) {
                String message = String.format("Failed to load document from %s", path);
                if (log.isWarnEnabled()) {
                    log.warn(message, e);
                }
            }
        });
        return list;
    }


    public static List<Document> loadDocumentsRecursively(Path directoryPath) {
        return loadDocumentsRecursively(directoryPath, DOCUMENT_PARSER);
    }


    public static List<Document> loadDocumentsRecursively(String directoryPath) {
        return loadDocumentsRecursively(Paths.get(directoryPath));
    }

    public static List<Document> loadDocumentsRecursively(String directoryPath, DocumentParser documentParser) {
        return loadDocumentsRecursively(Paths.get(directoryPath), documentParser);
    }

    public static List<Document> loadDocumentsRecursively(Path directoryPath, DocumentParser documentParser) {
        return loadDocumentsRecursively(directoryPath, documentParser, path -> {
            return true;
        });
    }

    public static List<Document> loadDocumentsRecursively(Path directoryPath, DocumentParser documentParser, PathMatcher pathMatcher) {
        if (!Files.isDirectory(directoryPath)) {
            throw new IllegalArgumentException(String.format("%s is not a directory", directoryPath));
        }
        Stream<Path> pathStream = null;
        try {
            pathStream = Files.walk(directoryPath);
            return loadDocuments(pathStream, documentParser, pathMatcher, directoryPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (pathStream != null) {
                pathStream.close();
            }
        }
    }
}
